package com.xyzwps.server.paimon.annotation.processor;

import com.google.auto.service.AutoService;
import com.xyzwps.server.paimon.annotation.RestApis;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@SupportedAnnotationTypes("com.xyzwps.server.paimon.annotation.RestApis")
@SupportedSourceVersion(SourceVersion.RELEASE_21)
@AutoService(Processor.class)
public class RestApisProcessor extends AbstractProcessor {

    private static final String REST_APIS_CLASS_NAME = RestApis.class.getCanonicalName();

    private void log_info(String msg) {
        processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "[AP][" + this.getClass().getSimpleName() + "] " + msg);
    }

    private void log_warn(String msg) {
        processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "[AP][" + this.getClass().getSimpleName() + "] " + msg);
    }

    record ClassInfo(String className, RestApis restApis) {
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        var classNames = new ArrayList<ClassInfo>();

        for (TypeElement annotation : annotations) {
            if (REST_APIS_CLASS_NAME.equals(annotation.getQualifiedName().toString())) {
                var annotatedElements = roundEnv.getElementsAnnotatedWith(annotation);
                for (Element element : annotatedElements) {
                    if (element instanceof TypeElement te) {
//                        log_info("Found RestApis annotated class: " + te.getQualifiedName());
                        classNames.add(new ClassInfo(
                                te.getQualifiedName().toString(),
                                te.getAnnotation(RestApis.class)
                        ));
                    }
                }
            }
        }

        if (classNames.isEmpty()) {
            return false;
        }

        // Try to generate the configuration class
        try {
            String className = "RestApisConfiguration";
            String packageName = "com.xyzwps.server.paimon.gen.conf";

            // Create class file
            JavaFileObject builderFile = processingEnv.getFiler().createSourceFile(packageName + "." + className);

            var classesToImport = new ArrayList<>();
            classNames.forEach(c -> classesToImport.add(c.className));
            classesToImport.addAll(List.of(
                    "java.net.http.HttpClient",
                    "java.time.Duration",
                    "com.xyzwps.server.paimon.annotation.RestApis",
                    "org.slf4j.Logger",
                    "org.slf4j.LoggerFactory",
                    "org.springframework.beans.factory.annotation.Autowired",
                    "org.springframework.beans.factory.annotation.Value",
                    "org.springframework.context.annotation.Bean",
                    "org.springframework.context.annotation.Configuration",
                    "org.springframework.http.client.JdkClientHttpRequestFactory",
                    "org.springframework.web.client.RestClient",
                    "org.springframework.web.client.support.RestClientAdapter",
                    "org.springframework.web.service.invoker.HttpServiceProxyFactory"
            ));

            // write class file
            try (var out = new PrintWriter(builderFile.openWriter())) {
                out.println("package " + packageName + ";");
                out.println();
                for (var cn : classesToImport) {
                    out.println("import " + cn + ";");
                }
                out.println();
                out.println("@Configuration");
                out.println("public class " + className + " {");
                out.println();
                out.println("""
                            private static final Logger log = LoggerFactory.getLogger(RestApisConfiguration.class);
                        
                            @Value("${rest-apis.connect-timeout:PT5S}")
                            private Duration connectTimeout;
                        
                            @Value("${rest-apis.read-timeout:PT5S}")
                            private Duration readTimeout;
                        
                            private RestClient restClient(String baseUrl) {
                                var connectTimeout = Duration.ofSeconds(5);
                                var readTimeout = Duration.ofSeconds(5);
                                var httpClient = HttpClient.newBuilder()
                                        .version(HttpClient.Version.HTTP_1_1)
                                        .connectTimeout(connectTimeout)
                                        .build();
                                var httpRequestFactory = new JdkClientHttpRequestFactory(httpClient);
                                httpRequestFactory.setReadTimeout(readTimeout);
                                return RestClient.builder()
                                        .requestFactory(httpRequestFactory)
                                        .baseUrl(baseUrl)
                                        .build();
                            }
                        
                            private HttpServiceProxyFactory httpServiceProxyFactory(String baseUrl) {
                                var adapter = RestClientAdapter.create(restClient(baseUrl));
                                return HttpServiceProxyFactory.builderFor(adapter).build();
                            }
                        """);

                for (var cn : classNames) {
                    out.println(genBeanDef(cn));
                }

                out.println("}");
            }
        } catch (Exception e) {
            log_warn("Failed to generate RestApisConfiguration class");
        }

        return false;
    }

    private String genBeanDef(ClassInfo ci) {
        var cn = ci.className;
        var last = cn.lastIndexOf(".");
        var simpleName = cn.substring(last + 1);
        var beanName = simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1);

        return """
                    @Bean
                    public %s %s() {
                        var cls = %s.class;
                        log.info("[RestApis] init {}", cls.getCanonicalName());
                        var baseUrl = "%s";
                        return httpServiceProxyFactory(baseUrl).createClient(cls);
                    }
                """.formatted(simpleName, beanName, simpleName, ci.restApis.baseUrl());
    }
}
